package com.geese.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.geese.common.ErrorInfo;
import com.geese.common.ParamJsonModel;
import com.geese.filter.rsa.Base64Utils;
import com.geese.filter.rsa.MD5Util;
import com.geese.filter.rsa.RSAForPrivateCodec;
import com.geese.filter.rsa.RSAForPublicCodec;
import com.geese.util.MobileUtil;
import com.geese.util.ServletUtil;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * Created by Z先生 on 2017/2/22.
 */
public class SecurityFilter extends OncePerRequestFilter {

    private static Logger LOGGER = Logger.getLogger(SecurityFilter.class);

    /**
     * 白名单列表
     */
    private static List<String> whitelist = new ArrayList<String>(0);

    /**
     * 开启
     */
    private static final String OPEN = "1";

    /**
     * 关闭
     */
    private static final String CLOSE = "0";

    /**
     * 是否开启白名单校验(1开启，0关闭)，默认开启
     */
    private static String whiteListFlag = "1";

    /**
     * 验签开关(1开启，0关闭)，默认开启
     */
    private static String verifySignFlag = "1";

    private static RSAForPrivateCodec privateCode;

    private static RSAForPublicCodec publicCodec;

    private static List<String> interfaceWhiteList = new ArrayList<String>();


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //设置请求和响应的基本参数
        setParams(request, response);

        //过滤文件请求
       /* if (isFileRequest(request)) {
            filterChain.doFilter(request, response);
            return;
        }*/

        //IP白名单过滤
//        String ip = ServletUtil.getIpAddress(request);
//        if (!inIpWhitelist(ip)) {
//            LOGGER.error("该IP无法访问服务器:" + ip);
//            printJson(ErrorInfo.CODE_IP_REFUSED, ErrorInfo.MSG_IP_REFUSED, response);
//            return;
//        }


        //获取ua，用来判断是否为移动端访问
      /*  String userAgent = request.getHeader("USER-AGENT").toLowerCase();
        if (MobileUtil.check(userAgent) && verifySignFlag.equals("1")) {
            //移动端延签
            verify(response, getJsonModel(request.getInputStream()));
        } else {
            //网页端延签
        }*/


        filterChain.doFilter(request, response);
    }

    /**
     * 封装请求数据
     */
    private ParamJsonModel getJsonModel(ServletInputStream inputStream) throws IOException {
        String json = IOUtils.toString(inputStream, "UTF-8");
        if (json == null || "".equals(json)) {
            return null;
        }
        return JSON.parseObject(json, ParamJsonModel.class);
    }

    /**
     * 验签
     */
    private boolean verify(HttpServletResponse response, ParamJsonModel jsonModel) throws IOException {
        if (jsonModel == null) {
            LOGGER.error(ErrorInfo.MSG_SYSTEM_ERROR);
            printJson(ErrorInfo.CODE_SYSTEM_ERROR, ErrorInfo.MSG_SYSTEM_ERROR, response);
            return false;
        }

        if (verifySignFlag.equals(OPEN)) {
            boolean verifyResult = false;
            try {
                verifyResult = verifySign(jsonModel);
            } catch (Exception e) {
                LOGGER.error(ErrorInfo.MSG_SYSTEM_ERROR, e);
                printJson(ErrorInfo.CODE_SYSTEM_ERROR, ErrorInfo.MSG_SYSTEM_ERROR, response);
                return false;
            }

            if (!verifyResult) {
                printJson(ErrorInfo.CODE_SIGN_INVALID, ErrorInfo.MSG_SIGN_INVALID, response);
                return false;
            }
        }

        return true;
    }

    private boolean verifySign(ParamJsonModel paramJsonModel) throws Exception {
        //加密过程
        //对data进行md5和base64加密,和其他数据拼接成再生成base64加密的sign

        String data = paramJsonModel.getData();
        String timestamp = paramJsonModel.getTimestamp();
        String nonce = paramJsonModel.getNonce();
        String sign = paramJsonModel.getSign();
        String token = paramJsonModel.getToken();
        String base64Data = MD5Util.MD5Base64(data);

        if (LOGGER.isDebugEnabled()){
            LOGGER.debug("data=" + data);
            LOGGER.debug("timestamp=" + timestamp);
            LOGGER.debug("nonce=" + nonce);
            LOGGER.debug("sign=" + sign);
        }

        TreeMap<String, String> param = new TreeMap<>();
        param.put("data", base64Data);
        param.put("nonce", nonce);
        param.put("timestamp", timestamp);
        param.put("token", token);



        byte[] jiemi = privateCode.decrypt(Base64Utils.getInstance().decoder(sign));
        String result =new String(jiemi);

        String originSign = createSign(param);

        return result.trim().equals(originSign.trim());
    }


    /**
     * 设置请求和响应的基本参数
     */
    private void setParams(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/json;charset=utf-8");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    }

    /**
     * IP是否在白名单中
     */
    private boolean inIpWhitelist(String ip) throws IOException {
        if (logger.isDebugEnabled()) {
            logger.debug("ip access: " + ip);
        }

        if (whiteListFlag.equals(OPEN)) {
            boolean inWhiteList = true;
            for (String whiteItem : whitelist) {
                if (whiteItem.equals(ip)) {
                    inWhiteList = true;
                    break;
                }
            }
            return inWhiteList;
        }
        return true;
    }

    /**
     * 是否是文件请求
     */
    private boolean isFileRequest(HttpServletRequest request) {
        if (!request.getServletPath().contains("shtml")) {
            if (logger.isDebugEnabled()) {
                logger.debug("文件请求:" + request.getServletPath());
            }
            return true;
        }
        return false;
    }


    public static void init(String whiteListFlag, String whiteListStr, String verifySignFlag, RSAForPrivateCodec privateCode, String interfaceWhiteListStr) {
        //初始化白名单列表
        if (whiteListStr == null || "".equals(whiteListStr)) {
            return;
        }
        String[] whiteArray = whiteListStr.split(",");
        if (whiteArray != null && whiteArray.length > 0) {
            SecurityFilter.whitelist = Arrays.asList(whiteArray);
        }

        SecurityFilter.whiteListFlag = whiteListFlag;

        SecurityFilter.verifySignFlag = verifySignFlag;

        SecurityFilter.privateCode = privateCode;

        //初始化接口白名单
        String[] list = interfaceWhiteListStr.split(",");
        if (list != null && list.length > 0) {
            for (String inter : list) {
                interfaceWhiteList.add(inter);
            }
        }

    }

    /**
     * 生成签名
     */
    public static String createSign(SortedMap<String, String> params) {
        try {
            StringBuffer sb = new StringBuffer();
            // 字符串排序
            Arrays.sort(params.keySet().toArray(new String[0]));

            Iterator<Map.Entry<String, String>> iter = params.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String, String> entry = iter.next();
                if (null != entry.getValue() && !"".equals(entry.getValue())) {
                    sb.append(entry.getKey() + "=" + entry.getValue() + "&");
                }
            }
            return sb.substring(0, sb.length() - 1);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 输出JSON响应数据
     */
    private void printJson(int code, String message, HttpServletResponse response) throws IOException {
        Map resultMap = new HashMap();
        resultMap.put("code", code);
        resultMap.put("msg", message);
        printJson(response, getJsonStringOfObj(resultMap));
    }

    /**
     * 输出JSON响应数据
     */
    private void printJson(HttpServletResponse response, String json) throws IOException {
        PrintWriter out = response.getWriter();
        out.print(json);
    }

    /**
     * 序列化json对象
     *
     * @return
     */
    private String getJsonStringOfObj(Object obj) {
        SerializeWriter sw = new SerializeWriter();
        JSONSerializer serializer = new JSONSerializer(sw);
        serializer.write(obj);
        return sw.toString();
    }
}
